/****************************************************************************
**
**	File Name   : CmdLine.h
**
**	Project     : DatChecker
**
**	Last Updated: $Header: /DatChecker.root/DatChecker/CmdLine.h 2     2/27/04 9:13a Neusel $
**
**	Description : 
**
**		CmdLine class interface and implementation.  Portions based on 
**		CStringParser as published at www.codeproject.com
**
****************************************************************************/
#ifndef	H_CMDLINE
	#define H_CMDLINE	__TIMESTAMP__

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#	pragma once
#endif
/*	----------------------------------------------------------------------
	Local Constants/Variables
	----------------------------------------------------------------------	*/
#define	IS_EMPTY(p)		(*(p) == '\0')
#define	IS_QUOTE(p)		(*(p) == '\"')
#define	IS_SLASH(p)		(*(p) == '\\')
#define	IS_SPACE(p)		(*(p) == ' ' || *(p) == '\t')
/*	----------------------------------------------------------------------
	CCmdLine class interface -- no implementation file
	----------------------------------------------------------------------	*/
class CCmdLine
	{
/*
**	Variables
*/
protected:
	TCHAR **			m_pArgs;
	BYTE *				m_pData;
	SIZE_T				m_nArgs;
/*
**	Methods
*/
protected:
	int
	parse(LPCTSTR p)
		{
		bool	bCopyChar	= true,     /* true = copy char to *pszArgs */
				bInQuote	= false;    /* true = inside quotes */
		int		nArgs		= 0,
				nLen		= 0,
				nSlashes	= 0;		/* num of backslashes seen */
		LPTSTR	pszArgs		= reinterpret_cast<LPTSTR>(m_pData);
		
		if (NULL_PTR(p) || IS_EMPTY(p)) 
			{
			if (GOOD_PTR(m_pArgs)) 
				m_pArgs[0] = NULL;

			m_nArgs = 0;
			return 0;
			}
		//
		// Check the first character to see if the whole command line is one big
		// quoted string.
		//
		if (IS_QUOTE(p))
			{
			p++;	// skip over the opening quote
			bInQuote = true;
			}

		for (nArgs = 0; !IS_EMPTY(p); ++nArgs)	//	Loop through each of the arguments
			{
			if (GOOD_PTR(m_pArgs))
				m_pArgs[nArgs] = pszArgs;

			for (;;) 	//	Loop through this argument
				{     
				bCopyChar = true;
				nSlashes = 0;
				/* 
				** Rules:	2N backslashes + " ==> N backslashes and begin/end quote
				**			2N+1 backslashes + " ==> N backslashes + literal "
				**			N backslashes ==> N backslashes 
				*/				
				if (IS_SLASH(p))
					{
					do 
						{ 
						p++; 
						nSlashes++;
						} 
					while (IS_SLASH(p));
					}
				/*
				** If 2N backslashes before, start/end quote, otherwise
				** copy literally 
				*/
				if (IS_QUOTE(p)) 
					{
					if ((nSlashes % 2) == 0) 
						{
						if (bInQuote) 
							{
							if (p[1] == '\"')
								p++;    // Double quote inside quoted string 
							else	// skip first quote char and copy second
								bCopyChar = false;
							} 
						else
							bCopyChar = false;
						
						bInQuote = !bInQuote;
						}

					nSlashes /= 2;          /* divide nSlashes by two */
					}
				//
				//	Now copy the slashes (if any)
				//
				while (nSlashes-- > 0) 
					{
					if (pszArgs)
						*pszArgs++ = '\\';
					nLen++;
					}
				//
				//	Quit at end of argument
				//
				if (IS_EMPTY(p))
					break;

				if (!bInQuote && IS_SPACE(p))
					{
					do
						p++;
					while (IS_SPACE(p));
					break;	//	Eat all the leading spaces and give up
					}
				//
				//	Copy character into argument
				//
				if (bCopyChar) 
					{
					if (GOOD_PTR(pszArgs))
						*pszArgs++ = *p;
					nLen++;
					}

				p++;
				}

			nLen++;
			if (GOOD_PTR(pszArgs))
				*pszArgs++ = '\0';	//	Make sure the argument is zero-terminated
				
			ATLASSERT( nLen < MB(1) );	// watch out for endless loops...
			}

		m_nArgs = nArgs;
		
		return nLen * sizeof(TCHAR);
		}

public:
	void				
	Empty(void)
		{
		delete [] m_pArgs;
		delete [] m_pData;
		m_pArgs = NULL;
		m_pData	= NULL;
		m_nArgs	= 0;
		}	

	int					
	Parse(LPCTSTR pszCmd)
		{
		Empty();

		if (NULL_PTR(pszCmd))
			{
			::SetLastError(ERROR_INVALID_PARAMETER);
			return 0;
			}
		//
		//	Now actually go parse the command line to find out (1) how many
		//	arguments there are (m_pArgs[]) and (2) the total amount of
		//	string space required (m_pData).
		//
		SIZE_T	sBytes = parse(pszCmd);
		
		if (m_nArgs > 0)
			{
			ATLTRACE("CCmdLine::Parse() => allocating %lu arg ptrs\n", m_nArgs);
			
			m_pArgs = new LPTSTR[m_nArgs];
			if (GOOD_PTR(m_pArgs))
				::ZeroMemory(m_pArgs, sizeof(LPTSTR) * m_nArgs);
			else
				{
				::SetLastError(ERROR_OUTOFMEMORY);
				return 0;
				}
			}

		if (sBytes > 0)
			{
			ATLTRACE("CCmdLine::Parse() => allocating %lu characters\n", sBytes);
			
			m_pData = new BYTE[sBytes];
			if (GOOD_PTR(m_pData))
				::ZeroMemory(m_pData, sBytes);
			else
				{
				::SetLastError(ERROR_OUTOFMEMORY);
				return 0;
				}
			}
		//
		//	Now that the buffer is allocated, parse a second time into the buffer
		//
		(void) parse(pszCmd);
						
		return m_nArgs;
		}
//
//	GETTERS/SETTERS
//
	LPTSTR				GetAt(SIZE_T ndx) const
							{
							return (GOOD_PTR(m_pArgs) && ndx < m_nArgs) ? m_pArgs[ndx] : NULL;
							}

	SIZE_T				GetCount(void) const
							{ return m_nArgs; }
//
//	CTOR/DTOR
//
						CCmdLine(LPCTSTR pstrCmd = NULL) : m_pArgs(NULL), m_pData(NULL), m_nArgs(0)
							{ 
							if (GOOD_PTR(pstrCmd)) 
								(void) Parse(pstrCmd); 
							}

	virtual				~CCmdLine()
							{
							Empty(); 
							m_pArgs = NULL;
							m_pData = NULL; 
							}
/*
**	WTL VooDoo Code
*/
	};

#endif	// H_CMDLINE
/****************************************************************************
**
**	$History: CmdLine.h $
 * 
 * *****************  Version 2  *****************
 * User: Neusel       Date: 2/27/04    Time: 9:13a
 * Updated in $/DatChecker.root/DatChecker
 * Fixed bogus header/footer SourceSafe keywords.
**
**	-------------------------------------------------------------------------
**
**	End of CmdLine.h
**
****************************************************************************/
